Skip to content

Conversation

@ogzhanolguncu
Copy link
Contributor

@ogzhanolguncu ogzhanolguncu commented Oct 23, 2025

What does this PR do?

This PR simplifies casting aggregated json data to concrete types. Also, replaces []byte override with json.RawMessage because it represents our JSON Db columns better. Regarding dynamically casting interface{} to associated type, I tried to override them in sqlc.json, but since those values are aggregated and not really a column in the db, override fails. I also tried to do plugin but cons are overweighed the pros and finally I tried to do replace them using a script then I ended up with too much magic and very error prone code. UnmarshalJSONArray this is the most idiomatic way to solve this.

https://linear.app/unkey/issue/ENG-1991/improve-type-safety-for-json-columns-in-database-structs

Fixes #3789

If there is not an issue for this, please create one first. This is used to tracking purposes and also helps use understand why this PR exists

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

  • Make sure tests are passing

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

@linear
Copy link

linear bot commented Oct 23, 2025

@changeset-bot
Copy link

changeset-bot bot commented Oct 23, 2025

⚠️ No Changeset found

Latest commit: dc4fd8f

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link

vercel bot commented Oct 23, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
dashboard Ignored Ignored Preview Nov 6, 2025 2:14pm
engineering Ignored Ignored Preview Nov 6, 2025 2:14pm

@ogzhanolguncu ogzhanolguncu changed the title refactor: make marshalling easier and override json to RawMessage refactor: make unmarshalling easier and override json to RawMessage Oct 23, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 23, 2025

📝 Walkthrough

Walkthrough

Adds a generic DB helper UnmarshalNullableJSONTo[T any] and replaces scattered json.Unmarshal usages across key/identity handlers and tests with calls to this helper; updates minor local variable naming and groups a few type alias declarations.

Changes

Cohort / File(s) Summary
Core DB utility
go/pkg/db/custom_types.go
Adds exported generic UnmarshalNullableJSONTo[T any](data any) (T, error) that accepts any, treats nil/empty as zero value, handles []byte and string, and unmarshals JSON into T. Updates RoleInfo comment.
DB key-data parsing
go/pkg/db/key_data.go
Replaces per-field json.Unmarshal blocks with UnmarshalNullableJSONTo[...] for Roles, Permissions, RolePermissions, and Ratelimits in buildKeyDataFromKeySpace and buildKeyData; removes encoding/json import.
API — identities handlers
go/apps/api/routes/v2_identities_get_identity/handler.go, go/apps/api/routes/v2_identities_list_identities/handler.go, go/apps/api/routes/v2_identities_create_identity/200_test.go
Replaces manual JSON decoding of identity.Meta (and ratelimits in get_identity) with db.UnmarshalNullableJSONTo[map[string]any] (or typed slices), adds error logging on failure and continues with empty meta; test updated to call helper and assert no error.
API — keys & apis handlers
go/apps/api/routes/v2_keys_get_key/handler.go, go/apps/api/routes/v2_keys_verify_key/handler.go, go/apps/api/routes/v2_keys_whoami/handler.go, go/apps/api/routes/v2_apis_list_keys/handler.go
Replaces manual JSON decoding of key and identity Meta with db.UnmarshalNullableJSONTo[map[string]any], adds structured error logging (including keyId context), removes encoding/json imports, and changes some handlers to log-and-continue instead of returning on unmarshal errors; groups some type aliases into a single block.
Test utilities
go/pkg/testutil/seed/seed.go
Renames local variable identityIdidentityID inside CreateIdentity and updates downstream usages; no signature change.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Handler
  participant DBpkg as db.UnmarshalNullableJSONTo
  participant JSON as json.Unmarshal

  Client->>Handler: Request (load key/identity)
  Handler->>DBpkg: UnmarshalNullableJSONTo(data)
  DBpkg->>JSON: if data is []byte|string -> json.Unmarshal into T
  JSON-->>DBpkg: T or error
  DBpkg-->>Handler: (T, nil) or (zero T, error)
  Handler-->>Client: Response (meta set if unmarshal succeeded, else empty + logged)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Changes are consistent (centralizing JSON decoding) but touch multiple handlers and tests.
  • Pay extra attention to:
    • Correct zero-value behavior and nil-handling in UnmarshalNullableJSONTo.
    • Logging semantics vs. previous error-return behavior in handlers (ensure intended fault-tolerance).
    • Type parameters used at call sites (maps/slices) match expected response types.

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'refactor: make unmarshalling easier and override json to RawMessage' clearly summarizes the main change: introducing a helper function for easier JSON unmarshalling and addressing JSON type handling.
Description check ✅ Passed The PR description completes all required template sections: explains the change (simplifies JSON casting via UnmarshalNullableJSONTo), marks 'Enhancement' as the change type, provides test guidance, and checks off required checklist items.
Linked Issues check ✅ Passed The PR successfully addresses issue #3789 by introducing UnmarshalNullableJSONTo helper that reduces manual type assertions for JSON columns (roles, permissions, ratelimits) across multiple handlers, improving type safety and downstream code ergonomics.
Out of Scope Changes check ✅ Passed All changes are scoped to the issue: new helper function in custom_types.go, usage replacements in handlers and key_data.go for JSON unmarshalling, variable naming cleanup in seed.go, and type declaration reformatting—all directly supporting improved JSON unmarshalling ergonomics.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ENG-1991

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 858b26a and dc4fd8f.

📒 Files selected for processing (2)
  • go/apps/api/routes/v2_keys_get_key/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_whoami/handler.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • go/apps/api/routes/v2_keys_whoami/handler.go
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 4190
File: go/internal/services/keys/verifier.go:51-53
Timestamp: 2025-10-30T15:10:52.743Z
Learning: PR #4190 for unkeyed/unkey is focused solely on database schema and query changes for identity-based credits. It adds IdentityCredits and KeyCredits fields to structs and queries, but does not implement the priority enforcement logic in the usagelimiter. The logic implementation is intentionally deferred to a later PR in the stack.
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3785
File: go/apps/api/routes/v2_keys_reroll_key/401_test.go:52-61
Timestamp: 2025-08-14T16:25:48.167Z
Learning: User Flo4604 requested creation of a GitHub issue to track converting all test files to use table-driven test patterns as a broader codebase improvement, following the suggestion made during review of go/apps/api/routes/v2_keys_reroll_key/401_test.go.
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: Repo: unkeyed/unkey — Preference: If imeyer comments “issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3753
File: go/apps/api/openapi/config.yaml:9-10
Timestamp: 2025-08-08T15:09:01.312Z
Learning: Repo unkeyed/unkey: For go/apps/api/openapi, oapi-codegen is used and does not support OpenAPI 3.1 union types like [T, "null"]; an overlay step is required to downconvert to 3.0-style nullable before code generation.
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 3297
File: apps/dashboard/lib/trpc/routers/authorization/roles/query.ts:210-323
Timestamp: 2025-06-04T20:13:12.060Z
Learning: The user ogzhanolguncu prefers explicit, duplicated code over abstracted helper functions when it improves readability, even if it means some duplication in filter building functions in the authorization roles query module.
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 2825
File: apps/dashboard/app/(app)/logs-v2/hooks/use-bookmarked-filters.ts:0-0
Timestamp: 2025-01-30T20:51:44.359Z
Learning: The user (ogzhanolguncu) prefers to handle refactoring suggestions in separate PRs to maintain focus in the current PR.
📚 Learning: 2025-08-08T15:09:01.312Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3753
File: go/apps/api/openapi/config.yaml:9-10
Timestamp: 2025-08-08T15:09:01.312Z
Learning: Repo unkeyed/unkey: For go/apps/api/openapi, oapi-codegen is used and does not support OpenAPI 3.1 union types like [T, "null"]; an overlay step is required to downconvert to 3.0-style nullable before code generation.

Applied to files:

  • go/apps/api/routes/v2_keys_get_key/handler.go
📚 Learning: 2025-10-30T15:10:52.743Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 4190
File: go/internal/services/keys/verifier.go:51-53
Timestamp: 2025-10-30T15:10:52.743Z
Learning: PR #4190 for unkeyed/unkey is focused solely on database schema and query changes for identity-based credits. It adds IdentityCredits and KeyCredits fields to structs and queries, but does not implement the priority enforcement logic in the usagelimiter. The logic implementation is intentionally deferred to a later PR in the stack.

Applied to files:

  • go/apps/api/routes/v2_keys_get_key/handler.go
📚 Learning: 2025-08-08T15:09:01.312Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3753
File: go/apps/api/openapi/config.yaml:9-10
Timestamp: 2025-08-08T15:09:01.312Z
Learning: Repo unkeyed/unkey: In go/apps/api/openapi, oapi-codegen doesn’t support OpenAPI 3.1 union nullability; overlay.yaml must be applied before codegen. The overlay key in oapi-codegen config isn’t supported—use a pre-step (programmatic or CLI) to merge overlay into the bundled spec, then run oapi-codegen.

Applied to files:

  • go/apps/api/routes/v2_keys_get_key/handler.go
📚 Learning: 2025-08-08T15:09:01.312Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3753
File: go/apps/api/openapi/config.yaml:9-10
Timestamp: 2025-08-08T15:09:01.312Z
Learning: Repo unkeyed/unkey: In go/apps/api/openapi, overlays are required to downconvert 3.1 union nullability to 3.0-style nullable before running oapi-codegen; config.yaml’s output-options.overlay is not recognized by oapi-codegen, so overlays must be applied in a pre-step (programmatic or CLI) prior to codegen.

Applied to files:

  • go/apps/api/routes/v2_keys_get_key/handler.go
🧬 Code graph analysis (1)
go/apps/api/routes/v2_keys_get_key/handler.go (3)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (54-80)
go/apps/api/openapi/gen.go (2)
  • Identity (161-173)
  • Meta (279-282)
go/pkg/db/models_generated.go (2)
  • Identity (643-652)
  • Key (654-678)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Test Go API Local / Test
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Build / Build
  • GitHub Check: Analyze (javascript-typescript)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ogzhanolguncu ogzhanolguncu marked this pull request as ready for review October 23, 2025 14:21
@Flo4604
Copy link
Member

Flo4604 commented Oct 23, 2025

Using json.rawmessage with inserts is a pain, that's why we are using []byte in the first place

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 34a8f42 and a0230f0.

📒 Files selected for processing (9)
  • go/pkg/db/custom_types.go (2 hunks)
  • go/pkg/db/key_data.go (2 hunks)
  • go/pkg/db/key_find_for_verification.sql_generated.go (2 hunks)
  • go/pkg/db/key_find_live_by_hash.sql_generated.go (2 hunks)
  • go/pkg/db/key_find_live_by_id.sql_generated.go (2 hunks)
  • go/pkg/db/key_list_by_key_auth_id.sql_generated.go (2 hunks)
  • go/pkg/db/key_list_live_by_auth_id.sql_generated.go (2 hunks)
  • go/pkg/db/models_generated.go (4 hunks)
  • go/pkg/db/sqlc.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
go/pkg/db/key_data.go (1)
go/pkg/db/custom_types.go (4)
  • UnmarshalJSONArray (51-66)
  • RoleInfo (11-15)
  • PermissionInfo (17-22)
  • RatelimitInfo (24-32)
go/pkg/db/key_find_live_by_hash.sql_generated.go (1)
go/pkg/db/models_generated.go (1)
  • Workspace (814-832)
go/pkg/db/key_list_by_key_auth_id.sql_generated.go (1)
go/pkg/db/models_generated.go (2)
  • Key (640-664)
  • EncryptedKey (609-616)
go/pkg/db/key_find_live_by_id.sql_generated.go (1)
go/pkg/db/models_generated.go (4)
  • Api (514-525)
  • KeyAuth (666-677)
  • Workspace (814-832)
  • EncryptedKey (609-616)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build / Build
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Go API Local / Test
🔇 Additional comments (18)
go/pkg/db/sqlc.json (1)

36-43: LGTM - Idiomatic JSON handling.

The override mapping from JSON database type to json.RawMessage is the idiomatic Go approach for representing JSON data. While json.RawMessage is essentially []byte, it explicitly signals JSON intent and integrates better with the encoding/json package.

go/pkg/db/key_find_for_verification.sql_generated.go (2)

8-12: Generated code aligns with configuration.

The addition of encoding/json import and the updated IdentityMeta field type to json.RawMessage correctly reflect the sqlc configuration override.


93-120: Type mapping correctly applied.

The struct correctly uses json.RawMessage for IdentityMeta (line 116) while aggregated fields like Roles, Permissions, and Ratelimits remain as interface{} since they're computed columns from subqueries, not actual database columns. This aligns with the PR's stated approach.

go/pkg/db/key_find_live_by_id.sql_generated.go (2)

8-12: Consistent type handling.

Import and type changes align with the project-wide JSON handling refactor.


106-142: Struct types correctly updated.

The IdentityMeta field now uses json.RawMessage (line 135), consistent with the sqlc override. Aggregated relationship fields (Roles, Permissions, RolePermissions, Ratelimits) appropriately remain as interface{} per the design constraints.

go/pkg/db/key_list_by_key_auth_id.sql_generated.go (2)

8-12: Import added for JSON handling.

Consistent with the JSON type refactor across generated files.


41-48: Type mapping applied correctly.

The IdentityMeta field uses json.RawMessage (line 45), matching the global sqlc override for JSON columns.

go/pkg/db/key_list_live_by_auth_id.sql_generated.go (2)

8-12: Import addition supports JSON handling.

Consistent with other generated files in this refactor.


117-150: Field types correctly mapped.

The IdentityMeta field (line 143) correctly uses json.RawMessage. Aggregated fields remain interface{} as expected for computed JSON columns.

go/pkg/db/key_find_live_by_hash.sql_generated.go (2)

8-12: JSON import added.

Supports the json.RawMessage type used in the struct.


105-141: Type mappings consistent.

The IdentityMeta field (line 134) uses json.RawMessage. Aggregated JSON columns appropriately remain interface{}.

go/pkg/db/custom_types.go (1)

3-7: Import added for JSON unmarshalling.

The encoding/json package supports the new UnmarshalJSONArray helper function.

go/pkg/db/key_data.go (2)

84-88: Cleaner JSON array deserialization.

Replacing nil initialization with UnmarshalJSONArray calls centralizes the unmarshalling logic and ensures consistent handling of JSON-aggregated fields. The helper always returns a valid (non-nil) slice, eliminating potential nil pointer issues.


134-138: Consistent pattern applied.

Both buildKeyData and buildKeyDataFromKeyAuth now use the same UnmarshalJSONArray approach for deserializing relationship fields, improving maintainability.

go/pkg/db/models_generated.go (4)

555-567: Change from []byte to json.RawMessage for AuditLogTarget.Meta is verified as safe.

Downstream analysis found only three references to AuditLogTarget in test code (in go/apps/api/routes/v2_ratelimit_limit/200_test.go), accessing .Type, .ID, and .Name.String fields—not the Meta field. This confirms no breaking changes or required updates. The json.RawMessage type is already established in the codebase as the standard pattern for JSON columns, making this change safe and correct.


629-638: LGTM! Type change verified across all query result structs.

The change to json.RawMessage for Identity.Meta is correct and consistently applied. All query result struct IdentityMeta fields have been updated to match (verified in 5 generated SQL query files), and all downstream usages in handlers and tests are compatible since json.RawMessage is a standard Go type alias for []byte.


527-543: LGTM! No breaking changes found.

The change from []byte to json.RawMessage is compatible with all downstream code. The type flow is correct:

  • Source auditlog.AuditLog.ActorMeta is map[string]any
  • Converted via json.Marshal() to []byte in insert.go:69
  • Implicitly assigned to db.AuditLog.ActorMeta as json.RawMessage (which is defined as type RawMessage []byte)

This is the idiomatic pattern for JSON database columns. All SQL-generated code and service code handle the field correctly without modification.


814-832: No breaking changes—field is unused in codebase.

The Subscriptions field is not referenced anywhere in the application code, tests, or migrations. Comprehensive searches across all Go files (excluding generated files) returned zero results for field access, struct initialization, or serialization patterns. The change from []byte to json.RawMessage is safe and idiomatic—both types are compatible at the database level, and json.RawMessage is the Go standard for deferred JSON parsing. Since this is a sqlc-generated model file, the type change likely originates from an updated type mapping in the generation configuration, not manual code modification.

Likely an incorrect or invalid review comment.

@ogzhanolguncu
Copy link
Contributor Author

Using json.rawmessage with inserts is a pain, that's why we are using []byte in the first place

Wanna elaborate?

Copy link
Member

Flo4604 commented Oct 23, 2025

See the error messages in the failing tests

sql: Scan error on column index 11, name "subscriptions": unsupported Scan, storing driver.Value type into type *json.RawMessage

@github-actions
Copy link
Contributor

github-actions bot commented Oct 23, 2025

Thank you for following the naming conventions for pull request titles! 🙏

@ogzhanolguncu
Copy link
Contributor Author

ogzhanolguncu commented Oct 23, 2025

See the error messages in the failing tests

sql: Scan error on column index 11, name "subscriptions": unsupported Scan, storing driver.Value type into type *json.RawMessage

That's not what I meant why don't we stick to that after fixing those errors? Whats the trade off of []byte vs raw.JsonMessage?

Btw, I'm not saying you are wrong, but if its more idiomatic to use raw.Json thingy I say let's fix the issues and roll with this one

Copy link
Member

Flo4604 commented Oct 23, 2025

ah I remember why we use []byte json.rawmessage doesn't support null JSON columns

json.RawMessage does not support NULL · Issue #129 · sqlc-dev/sqlc

So you will have to work around this

@ogzhanolguncu
Copy link
Contributor Author

@Flo4604 I'll check again if its really causing too much headache i'll revert it back to []byte and let you know 🫡 . Thanks for the explanation

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (5)
go/apps/api/routes/v2_identities_create_identity/200_test.go (1)

103-118: Consider adding explicit NULL metadata test coverage.

Given the PR's focus on improving NULL JSON column handling (see PR comments about json.RawMessage NULL support issues), consider adding a test that creates an identity without metadata and explicitly verifies that identity.Meta can be safely unmarshaled or is properly represented as NULL/empty.

Currently, this test creates an identity without metadata but doesn't verify the Meta field behavior.

Add a test case that verifies NULL metadata handling:

t.Run("create identity without metadata and verify NULL handling", func(t *testing.T) {
	externalTestID := uid.New("test_external_id")
	req := handler.Request{ExternalId: externalTestID}
	res := testutil.CallRoute[handler.Request, handler.Response](h, route, headers, req)

	require.Equal(t, 200, res.Status)

	identity, err := db.Query.FindIdentityByExternalID(ctx, h.DB.RO(), db.FindIdentityByExternalIDParams{
		WorkspaceID: h.Resources().UserWorkspace.ID,
		ExternalID:  externalTestID,
		Deleted:     false,
	})
	require.NoError(t, err)
	
	// Verify NULL or empty metadata is handled correctly
	dbMeta, err := db.UnmarshalNullableJSONTo[map[string]any](&identity.Meta)
	require.NoError(t, err)
	// Assert expected behavior for NULL/empty metadata
	// (adjust assertion based on your NullJSON implementation)
})
go/pkg/db/sqlc.json (1)

36-55: Consider a second override for non-nullable JSON → json.RawMessage.

To avoid type fragmentation and keep non-nullable JSON fields strongly typed, add an override for nullable=false mapping to encoding/json.RawMessage.

Apply:

           "overrides": [
             {
               "db_type": "json",
               "go_type": {
                 "type": "NullJSON",
                 "package": "dbtype",
                 "import": "github.com/unkeyed/unkey/go/pkg/db/types"
               },
               "nullable": true
             },
+            {
+              "db_type": "json",
+              "go_type": {
+                "type": "RawMessage",
+                "package": "json",
+                "import": "encoding/json"
+              },
+              "nullable": false
+            },
             {
               "column": "permissions.description",
               "go_type": {
                 "type": "NullString",
                 "package": "dbtype",
                 "import": "github.com/unkeyed/unkey/go/pkg/db/types"
               },
               "nullable": true
             }
           ]
go/pkg/db/key_data.go (1)

84-87: LGTM! Cleaner JSON unmarshalling with centralized helpers.

The refactoring replaces inline JSON unmarshalling with UnmarshalJSONArrayTo, which provides consistent error handling (returning empty slices on failure) across all relationship fields. This improves maintainability and reduces code duplication.

Also applies to: 134-137

go/apps/api/routes/v2_keys_verify_key/handler.go (1)

24-27: LGTM! Improved code organization.

Grouping type aliases into a type block is more idiomatic and cleaner.

go/apps/api/routes/v2_identities_list_identities/handler.go (1)

19-22: LGTM! Improved code organization.

Grouping type aliases is more idiomatic and consistent with other handlers in this PR.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a0230f0 and f968048.

📒 Files selected for processing (21)
  • go/apps/api/routes/v2_apis_list_keys/handler.go (1 hunks)
  • go/apps/api/routes/v2_identities_create_identity/200_test.go (4 hunks)
  • go/apps/api/routes/v2_identities_get_identity/handler.go (1 hunks)
  • go/apps/api/routes/v2_identities_list_identities/200_test.go (1 hunks)
  • go/apps/api/routes/v2_identities_list_identities/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_get_key/handler.go (1 hunks)
  • go/apps/api/routes/v2_keys_verify_key/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_whoami/handler.go (1 hunks)
  • go/pkg/db/custom_types.go (2 hunks)
  • go/pkg/db/identity_find_with_ratelimits.sql_generated.go (2 hunks)
  • go/pkg/db/key_data.go (2 hunks)
  • go/pkg/db/key_data_test.go (2 hunks)
  • go/pkg/db/key_find_for_verification.sql_generated.go (2 hunks)
  • go/pkg/db/key_find_live_by_hash.sql_generated.go (2 hunks)
  • go/pkg/db/key_find_live_by_id.sql_generated.go (2 hunks)
  • go/pkg/db/key_list_by_key_auth_id.sql_generated.go (2 hunks)
  • go/pkg/db/key_list_live_by_auth_id.sql_generated.go (2 hunks)
  • go/pkg/db/models_generated.go (4 hunks)
  • go/pkg/db/sqlc.json (1 hunks)
  • go/pkg/db/types/null_json.go (1 hunks)
  • go/pkg/testutil/seed/seed.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • go/pkg/db/key_find_live_by_id.sql_generated.go
  • go/pkg/db/key_list_by_key_auth_id.sql_generated.go
  • go/pkg/db/custom_types.go
  • go/pkg/db/key_list_live_by_auth_id.sql_generated.go
  • go/pkg/db/models_generated.go
🧰 Additional context used
🧬 Code graph analysis (13)
go/apps/api/routes/v2_identities_create_identity/200_test.go (1)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-83)
go/pkg/db/key_data.go (1)
go/pkg/db/custom_types.go (4)
  • UnmarshalJSONArrayTo (51-66)
  • RoleInfo (11-15)
  • PermissionInfo (17-22)
  • RatelimitInfo (24-32)
go/apps/api/routes/v2_keys_verify_key/handler.go (3)
go/apps/api/openapi/gen.go (2)
  • Identity (161-173)
  • Meta (279-282)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-83)
go/pkg/fault/wrap.go (1)
  • Wrap (25-67)
go/apps/api/routes/v2_apis_list_keys/handler.go (3)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-83)
go/pkg/db/models_generated.go (1)
  • Identity (629-638)
go/apps/api/openapi/gen.go (2)
  • Identity (161-173)
  • Meta (279-282)
go/apps/api/routes/v2_identities_list_identities/handler.go (2)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-83)
go/pkg/fault/wrap.go (3)
  • Wrap (25-67)
  • Internal (75-89)
  • Public (97-111)
go/pkg/db/identity_find_with_ratelimits.sql_generated.go (1)
go/pkg/db/types/null_json.go (1)
  • NullJSON (10-13)
go/apps/api/routes/v2_identities_get_identity/handler.go (2)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-83)
go/pkg/fault/wrap.go (3)
  • Wrap (25-67)
  • Internal (75-89)
  • Public (97-111)
go/pkg/db/key_data_test.go (2)
go/pkg/db/types/null_json.go (1)
  • NullJSON (10-13)
go/pkg/db/models_generated.go (1)
  • Identity (629-638)
go/pkg/db/key_find_live_by_hash.sql_generated.go (1)
go/pkg/db/types/null_json.go (1)
  • NullJSON (10-13)
go/apps/api/routes/v2_keys_get_key/handler.go (3)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-83)
go/pkg/db/models_generated.go (1)
  • Identity (629-638)
go/apps/api/openapi/gen.go (2)
  • Identity (161-173)
  • Meta (279-282)
go/pkg/testutil/seed/seed.go (3)
go/pkg/db/types/null_json.go (1)
  • NullJSON (10-13)
go/pkg/db/identity_insert.sql_generated.go (1)
  • InsertIdentityParams (31-38)
go/pkg/db/models_generated.go (2)
  • Environment (618-627)
  • Identity (629-638)
go/apps/api/routes/v2_keys_whoami/handler.go (3)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-83)
go/pkg/db/models_generated.go (1)
  • Identity (629-638)
go/apps/api/openapi/gen.go (2)
  • Identity (161-173)
  • Meta (279-282)
go/pkg/db/key_find_for_verification.sql_generated.go (1)
go/pkg/db/types/null_json.go (1)
  • NullJSON (10-13)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Go API Local / Test
  • GitHub Check: Build / Build
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (15)
go/apps/api/routes/v2_identities_create_identity/200_test.go (1)

141-143: LGTM! Correct usage of the centralized JSON unmarshal helper.

The replacement of direct json.Unmarshal with db.UnmarshalNullableJSONTo is correct and follows the new pattern introduced in this PR. Error handling is properly preserved.

go/apps/api/routes/v2_identities_list_identities/200_test.go (1)

372-377: LGTM; aligns with NullJSON shape.

Using Meta.Valid and comparing against NullJSON.Data with JSONEq is correct and order‑insensitive.

go/pkg/db/identity_find_with_ratelimits.sql_generated.go (1)

12-13: Verification complete—NullJSON integration is correct.

The struct confirms Meta is dbtype.NullJSON (line 73) and Scan properly handles NULL (returns Valid=false) and valid JSON (with a defensive buffer copy to prevent reuse corruption). Ratelimits correctly remains interface{} as a non-database field. All requirements are met.

go/pkg/db/sqlc.json (1)

40-42: NullJSON properly implements Scanner/Valuer; no issues found.

Verification confirms that dbtype.NullJSON correctly implements both database/sql.Scanner and driver.Valuer interfaces with proper NULL handling. The Scan() method includes a defensive buffer copy to prevent corruption from driver buffer reuse—a critical safeguard. The sqlc override configuration is sound: nullable JSON columns map to NullJSON for reads, while insert parameters use json.RawMessage for explicit values. This design is intentional and working correctly.

go/pkg/db/key_find_live_by_hash.sql_generated.go (1)

12-13: LGTM; NullJSON properly handles LEFT JOIN NULLs.

The NullJSON type has Data and Valid fields, and its Scan() method correctly sets Valid=false when the database value is NULL. The UnmarshalTo() method returns nil without attempting to unmarshal when !Valid, preventing panics on NULL identity metadata from LEFT JOINs. All handlers safely use UnmarshalNullableJSONTo(), which wraps this behavior. The import change is correct.

go/pkg/db/key_find_for_verification.sql_generated.go (1)

12-12: LGTM! Generated code reflects the new NullJSON type.

The sqlc-generated code correctly imports the dbtype package and updates IdentityMeta to use dbtype.NullJSON, which properly handles nullable JSON columns.

Also applies to: 117-117

go/pkg/testutil/seed/seed.go (2)

380-380: LGTM! Naming convention improved.

The rename from identityId to identityID follows Go naming conventions for acronyms, and all usages are correctly updated.

Also applies to: 392-392, 397-397


372-387: Type inconsistency confirmed: verify if this is intended design.

The verification confirms that InsertIdentityParams.Meta is json.RawMessage while Identity.Meta is dbtype.NullJSON. Since both files are auto-generated, this pattern likely reflects intentional design: simpler raw types for insertion parameters and wrapped nullable types for model retrieval. However, the architectural intention needs confirmation—specifically whether the codebase applies this pattern consistently across other insert operations versus model definitions, and whether it introduces any conversion overhead or maintenance complexity that should be addressed at the code generator level.

go/apps/api/routes/v2_keys_verify_key/handler.go (1)

232-238: LGTM! Consistent error handling with centralized helper.

The refactoring replaces manual JSON unmarshalling with db.UnmarshalNullableJSONTo, maintaining identical error handling behavior while improving code consistency across the codebase.

go/pkg/db/types/null_json.go (3)

15-40: LGTM! Critical defensive copy prevents corruption.

The defensive copy in the Scan method is essential to prevent data corruption from driver buffer reuse. This is a well-known issue with json.RawMessage and the implementation correctly addresses it with clear documentation.


42-47: LGTM! Correct SQL driver Value implementation.

The Value method correctly returns nil for SQL NULL when the field is invalid, and properly converts the RawMessage to []byte when valid.


60-65: The silent no-op behavior is safe and works as intended.

The UnmarshalTo method is only called through the UnmarshalNullableJSONTo wrapper function (custom_types.go:79-83), which properly propagates the error to callers. All production usages check the returned error value. Returning nil when Valid is false is intentional and semantically correct: no error occurred, the field is null, and the caller receives a zero value—a reasonable default for nullable fields. The design properly distinguishes between actual unmarshaling errors (from json.Unmarshal) and null values.

go/apps/api/routes/v2_identities_list_identities/handler.go (1)

139-146: LGTM! Consistent error handling pattern.

The refactoring uses db.UnmarshalNullableJSONTo with proper error wrapping. Unlike the list_keys handler which only logs errors, this returns the error to the caller, which is more appropriate for an API that lists identities where metadata is expected.

go/pkg/db/key_data_test.go (1)

9-9: LGTM! Tests updated for new type.

The test correctly uses the new dbtype.NullJSON type for IdentityMeta, ensuring the type change is properly validated.

Also applies to: 125-125, 133-133

go/apps/api/routes/v2_apis_list_keys/handler.go (1)

306-311: Behavior change is intentional and consistent across handlers.

The old implementation checked both len(keyData.Identity.Meta) > 0 and identityMeta != nil before assigning, while the new code assigns &identityMeta whenever unmarshaling succeeds. This means empty JSON objects {} now result in a pointer to an empty map instead of nil.

However, this change is part of the broader refactor commit "refactor: use NullJSON for mysql json type" and follows the identical pattern already established in v2_keys_whoami and v2_keys_get_key handlers. The refactor also improves error handling by logging failures instead of silently ignoring them.

Confirm whether this behavior change is acceptable for the API contract (nil vs. empty map in the response).

@ogzhanolguncu
Copy link
Contributor Author

@Flo4604 I think I finally landed on a good solution, but there is some caveat

	// CRITICAL: A defensive copy to prevent buffer reuse corruption
	// json.RawMessage holds a reference to the underlying byte slice.
	// When the driver reuses that buffer for the next row, your NullJSON.Data suddenly points to corrupted memory.
	// This consumes more memory compared to copyless version and also guarantees data integrity.
	clone := make([]byte, len(buf))
	copy(clone, buf)

When scanning, I actually noticed this only during tests, data is getting corrupted due to how json.RawMessage handles the byte. wdyt? But overall usage is so much nicer, less clutter less noise.

image

Copy link
Member

Flo4604 commented Oct 24, 2025

why don't we then just use our byte[] version and add a helper that does the same thing?

Copy link
Member

Flo4604 commented Oct 24, 2025

db.Unmarshal[T any] (data []byte) (T, error) or so?

and if we wanted to copy the data we can just do identity.Meta instead of &identity.Meta

@ogzhanolguncu
Copy link
Contributor Author

I guess, yeah. copy also makes me feel uncomfortable

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
go/apps/api/routes/v2_keys_whoami/handler.go (1)

158-162: Honor omitempty by checking data presence before unmarshalling and setting Meta.

When keyData.Identity.Meta is nil or empty, the helper returns a zero-value map; taking &identityMeta creates a pointer to that nil map, which serializes as "meta": null instead of being omitted. Check for data presence first and only set response.Identity.Meta when valid data exists.

Apply the fix suggested in the previous review:

-		if identityMeta, err := db.UnmarshalNullableJSONTo[map[string]any](keyData.Identity.Meta); err != nil {
+		if len(keyData.Identity.Meta) > 0 {
+			identityMeta, err := db.UnmarshalNullableJSONTo[map[string]any](keyData.Identity.Meta)
+			if err != nil {
-			h.Logger.Error("failed to unmarshal identity meta", "error", err)
-		} else {
+				h.Logger.Error("failed to unmarshal identity meta", "error", err)
+			} else {
-			response.Identity.Meta = &identityMeta
+				response.Identity.Meta = &identityMeta
+			}
		}
go/apps/api/routes/v2_identities_get_identity/handler.go (1)

99-105: Log meta unmarshal errors instead of failing the request, and honor omitempty.

Failing the entire request when identity metadata cannot be parsed degrades UX. Other endpoints (e.g., v2_keys_whoami) log the error and continue. Additionally, always setting Meta: &metaMap produces "meta": null even when the DB column is empty or NULL; gate assignment on data presence to respect the omitempty tag.

Apply the fix suggested in the previous review:

-	metaMap, err := db.UnmarshalNullableJSONTo[map[string]any](identity.Meta)
-	if err != nil {
-		return fault.Wrap(err,
-			fault.Internal("unable to unmarshal metadata"),
-			fault.Public("We're unable to parse the identity's metadata."),
-		)
-	}
+	var metaPtr *map[string]any
+	if len(identity.Meta) > 0 {
+		metaMap, err := db.UnmarshalNullableJSONTo[map[string]any](identity.Meta)
+		if err != nil {
+			h.Logger.Error("failed to unmarshal identity meta", "error", err)
+		} else {
+			metaPtr = &metaMap
+		}
+	}

And update the response assignment:

		Data: openapi.Identity{
			Id:         identity.ID,
			ExternalId: identity.ExternalID,
-			Meta:       &metaMap,
+			Meta:       metaPtr,
			Ratelimits: &responseRatelimits,
		},
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f968048 and aef7136.

📒 Files selected for processing (9)
  • go/apps/api/routes/v2_apis_list_keys/handler.go (1 hunks)
  • go/apps/api/routes/v2_identities_create_identity/200_test.go (4 hunks)
  • go/apps/api/routes/v2_identities_get_identity/handler.go (1 hunks)
  • go/apps/api/routes/v2_identities_list_identities/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_get_key/handler.go (1 hunks)
  • go/apps/api/routes/v2_keys_verify_key/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_whoami/handler.go (1 hunks)
  • go/pkg/db/custom_types.go (2 hunks)
  • go/pkg/testutil/seed/seed.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • go/pkg/testutil/seed/seed.go
  • go/apps/api/routes/v2_apis_list_keys/handler.go
  • go/pkg/db/custom_types.go
  • go/apps/api/routes/v2_keys_get_key/handler.go
🧰 Additional context used
🧬 Code graph analysis (5)
go/apps/api/routes/v2_keys_verify_key/handler.go (4)
go/apps/api/openapi/gen.go (2)
  • Identity (161-173)
  • Meta (279-282)
go/pkg/db/models_generated.go (1)
  • Identity (629-638)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-94)
go/pkg/fault/wrap.go (3)
  • Wrap (25-67)
  • Internal (75-89)
  • Public (97-111)
go/apps/api/routes/v2_keys_whoami/handler.go (3)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-94)
go/pkg/db/models_generated.go (1)
  • Identity (629-638)
go/apps/api/openapi/gen.go (2)
  • Identity (161-173)
  • Meta (279-282)
go/apps/api/routes/v2_identities_create_identity/200_test.go (1)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-94)
go/apps/api/routes/v2_identities_list_identities/handler.go (3)
go/apps/api/openapi/gen.go (3)
  • V2IdentitiesListIdentitiesRequestBody (708-714)
  • V2IdentitiesListIdentitiesResponseBody (717-726)
  • Meta (279-282)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-94)
go/pkg/fault/wrap.go (3)
  • Wrap (25-67)
  • Internal (75-89)
  • Public (97-111)
go/apps/api/routes/v2_identities_get_identity/handler.go (2)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (79-94)
go/pkg/fault/wrap.go (3)
  • Wrap (25-67)
  • Internal (75-89)
  • Public (97-111)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Test Go API Local / Test
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Build / Build
🔇 Additional comments (3)
go/apps/api/routes/v2_identities_create_identity/200_test.go (1)

141-141: LGTM!

The test code correctly uses the new unmarshalling helper. The pattern is consistent across all test cases and appropriately validates metadata handling.

Also applies to: 241-241, 316-316, 425-425

go/apps/api/routes/v2_keys_verify_key/handler.go (1)

24-27: LGTM!

Grouping related type aliases in a single type block is idiomatic Go and improves readability.

go/apps/api/routes/v2_identities_list_identities/handler.go (1)

19-22: LGTM!

Grouping related type aliases in a single type block is idiomatic Go and improves readability.

Copy link
Member

Flo4604 commented Oct 24, 2025

yeah also we shouldn't fail when json parsing the meta object, as we discussed in slack the object should be valid since its validated as object when sending via the API and the frontend also validates it afaik.

Us failing this would block the user until we fix it which would be bad esp. if we do this in verify key and other endpoints

@ogzhanolguncu
Copy link
Contributor Author

yeah also we shouldn't fail when json parsing the meta object, as we discussed in slack the object should be valid since its validated as object when sending via the API and the frontend also validates it afaik.Us failing this would block the user until we fix it which would be bad esp. if we do this in verify key and other endpoints

Yeah I didn't change the existing behavior. We just log errors move on

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

♻️ Duplicate comments (1)
go/apps/api/routes/v2_identities_get_identity/handler.go (1)

99-121: Honor omitempty for Meta; set pointer only when meta is present.

Current code always sets Meta: &metaMap, yielding "meta": null when absent/invalid. Gate it so Meta is omitted unless parsed.

- metaMap := db.UnmarshalNullableJSONTo[map[string]any](identity.Meta, h.Logger)
+ var metaMap map[string]any
+ if identity.Meta != nil { // or check specific NullString/Valid shape if applicable
+     metaMap = db.UnmarshalNullableJSONTo[map[string]any](identity.Meta, h.Logger)
+ }
@@
-            Meta:       &metaMap,
+            Meta:       func() *map[string]any { if metaMap == nil { return nil }; return &metaMap }(),

Optional: also use the same helper for ratelimits for consistency/logging.

- var ratelimits []db.RatelimitInfo
- if ratelimitBytes, ok := identity.Ratelimits.([]byte); ok && ratelimitBytes != nil {
-     _ = json.Unmarshal(ratelimitBytes, &ratelimits)
- }
+ ratelimits := db.UnmarshalNullableJSONTo[[]db.RatelimitInfo](identity.Ratelimits, h.Logger)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ebe923b and bda915d.

📒 Files selected for processing (12)
  • go/apps/api/routes/v2_apis_list_keys/handler.go (2 hunks)
  • go/apps/api/routes/v2_identities_create_identity/200_test.go (4 hunks)
  • go/apps/api/routes/v2_identities_get_identity/handler.go (1 hunks)
  • go/apps/api/routes/v2_identities_list_identities/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_get_key/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_reroll_key/200_test.go (1 hunks)
  • go/apps/api/routes/v2_keys_reroll_key/handler.go (1 hunks)
  • go/apps/api/routes/v2_keys_verify_key/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_whoami/handler.go (2 hunks)
  • go/pkg/db/custom_types.go (2 hunks)
  • go/pkg/db/key_data.go (5 hunks)
  • go/pkg/db/key_data_test.go (12 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • go/apps/api/routes/v2_apis_list_keys/handler.go
  • go/apps/api/routes/v2_keys_get_key/handler.go
🧰 Additional context used
🧬 Code graph analysis (9)
go/apps/api/routes/v2_keys_verify_key/handler.go (3)
go/apps/api/openapi/gen.go (2)
  • Identity (161-173)
  • Meta (279-282)
go/pkg/db/models_generated.go (1)
  • Identity (629-638)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (57-86)
go/apps/api/routes/v2_keys_reroll_key/200_test.go (1)
go/pkg/db/key_data.go (1)
  • ToKeyData (30-47)
go/pkg/db/key_data.go (4)
go/pkg/db/key_find_live_by_hash.sql_generated.go (1)
  • FindLiveKeyByHashRow (104-140)
go/pkg/db/key_find_live_by_id.sql_generated.go (1)
  • FindLiveKeyByIDRow (105-141)
go/pkg/db/key_list_live_by_auth_id.sql_generated.go (1)
  • ListLiveKeysByKeyAuthIDRow (116-149)
go/pkg/db/custom_types.go (4)
  • UnmarshalNullableJSONTo (57-86)
  • RoleInfo (13-17)
  • PermissionInfo (19-24)
  • RatelimitInfo (26-34)
go/apps/api/routes/v2_identities_get_identity/handler.go (1)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (57-86)
go/pkg/db/key_data_test.go (1)
go/pkg/db/key_data.go (1)
  • ToKeyData (30-47)
go/apps/api/routes/v2_identities_create_identity/200_test.go (1)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (57-86)
go/apps/api/routes/v2_identities_list_identities/handler.go (2)
go/apps/api/openapi/gen.go (3)
  • V2IdentitiesListIdentitiesRequestBody (708-714)
  • V2IdentitiesListIdentitiesResponseBody (717-726)
  • Meta (279-282)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (57-86)
go/apps/api/routes/v2_keys_reroll_key/handler.go (1)
go/pkg/db/key_data.go (1)
  • ToKeyData (30-47)
go/apps/api/routes/v2_keys_whoami/handler.go (4)
go/pkg/db/key_data.go (1)
  • ToKeyData (30-47)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (57-86)
go/pkg/db/models_generated.go (1)
  • Identity (629-638)
go/apps/api/openapi/gen.go (2)
  • Identity (161-173)
  • Meta (279-282)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build / Build
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Go API Local / Test
🔇 Additional comments (1)
go/apps/api/routes/v2_keys_reroll_key/handler.go (1)

82-82: LGTM: pass logger into ToKeyData.

Signature updated correctly; aligns with helper-based JSON handling.

Copy link
Collaborator

@chronark chronark left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the logger really necessary?

@ogzhanolguncu
Copy link
Contributor Author

is the logger really necessary?

@chronark Can we just log it with regular log instead of our logger package. That would make everything super clean?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

♻️ Duplicate comments (2)
go/apps/api/routes/v2_keys_get_key/handler.go (1)

166-173: Check data validity and avoid emitting empty meta objects.

The code unmarshals keyData.Identity.Meta without first checking if data is present, and always assigns response.Identity.Meta = &identityMeta even when unmarshalling fails (resulting in an empty map). This produces "meta": {} instead of omitting the field per the omitempty tag.

Apply this diff to honor omitempty and align with other endpoints:

-		if identityMeta, err := db.UnmarshalNullableJSONTo[map[string]any](keyData.Identity.Meta); err != nil {
+		if len(keyData.Identity.Meta) > 0 {
+			identityMeta, err := db.UnmarshalNullableJSONTo[map[string]any](keyData.Identity.Meta)
+			if err != nil {
-			h.Logger.Error("failed to unmarshal identity meta",
-				"identityId", keyData.Identity.ID,
-				"error", err,
-			)
-		} else {
-			response.Identity.Meta = &identityMeta
+				h.Logger.Error("failed to unmarshal identity meta",
+					"identityId", keyData.Identity.ID,
+					"error", err,
+				)
+			} else if len(identityMeta) > 0 {
+				response.Identity.Meta = &identityMeta
+			}
 		}
go/apps/api/routes/v2_identities_list_identities/handler.go (1)

139-148: Only assign Meta when map is non-empty to honor omitempty.

Line 147 assigns newIdentity.Meta = &metaMap even when metaMap is an empty map (from nil/empty DB data), producing "meta": {} for identities with no metadata instead of omitting the field per the omitempty tag.

Apply:

 		metaMap, err := db.UnmarshalNullableJSONTo[map[string]any](identity.Meta)
 		if err != nil {
 			h.Logger.Error("failed to unmarshal identity meta",
 				"identityId", identity.ID,
 				"error", err,
 			)
 			// Continue with empty meta
-		} else {
+		} else if len(metaMap) > 0 {
 			newIdentity.Meta = &metaMap
 		}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 66be3ae and 8047e99.

📒 Files selected for processing (10)
  • go/apps/api/routes/v2_apis_list_keys/handler.go (2 hunks)
  • go/apps/api/routes/v2_identities_create_identity/200_test.go (4 hunks)
  • go/apps/api/routes/v2_identities_get_identity/handler.go (2 hunks)
  • go/apps/api/routes/v2_identities_list_identities/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_get_key/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_reroll_key/handler.go (0 hunks)
  • go/apps/api/routes/v2_keys_verify_key/handler.go (3 hunks)
  • go/apps/api/routes/v2_keys_whoami/handler.go (2 hunks)
  • go/pkg/db/custom_types.go (2 hunks)
  • go/pkg/db/key_data.go (2 hunks)
💤 Files with no reviewable changes (1)
  • go/apps/api/routes/v2_keys_reroll_key/handler.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • go/apps/api/routes/v2_keys_whoami/handler.go
  • go/apps/api/routes/v2_apis_list_keys/handler.go
🧰 Additional context used
🧠 Learnings (14)
📓 Common learnings
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 4190
File: go/internal/services/keys/verifier.go:51-53
Timestamp: 2025-10-30T15:10:52.743Z
Learning: PR #4190 for unkeyed/unkey is focused solely on database schema and query changes for identity-based credits. It adds IdentityCredits and KeyCredits fields to structs and queries, but does not implement the priority enforcement logic in the usagelimiter. The logic implementation is intentionally deferred to a later PR in the stack.
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3785
File: go/apps/api/routes/v2_keys_reroll_key/401_test.go:52-61
Timestamp: 2025-08-14T16:25:48.167Z
Learning: User Flo4604 requested creation of a GitHub issue to track converting all test files to use table-driven test patterns as a broader codebase improvement, following the suggestion made during review of go/apps/api/routes/v2_keys_reroll_key/401_test.go.
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: Repo: unkeyed/unkey — Preference: If imeyer comments “issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/workflows/job_detect_changes.yaml:111-114
Timestamp: 2025-08-08T16:10:00.224Z
Learning: Repo: unkeyed/unkey — When imeyer writes “make me an issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Repro, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3785
File: go/pkg/db/querier_generated.go:387-403
Timestamp: 2025-08-14T18:31:49.604Z
Learning: In MySQL's JSON_OBJECT function, boolean expressions like `rl.auto_apply = 1` automatically convert to proper JSON boolean values (true/false), not numeric values (0/1). This means Go's json.Unmarshal can correctly handle these fields when unmarshalling into bool types without any conversion issues.
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3753
File: go/apps/api/openapi/config.yaml:9-10
Timestamp: 2025-08-08T15:09:01.312Z
Learning: Repo unkeyed/unkey: For go/apps/api/openapi, oapi-codegen is used and does not support OpenAPI 3.1 union types like [T, "null"]; an overlay step is required to downconvert to 3.0-style nullable before code generation.
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 3297
File: apps/dashboard/lib/trpc/routers/authorization/roles/query.ts:210-323
Timestamp: 2025-06-04T20:13:12.060Z
Learning: The user ogzhanolguncu prefers explicit, duplicated code over abstracted helper functions when it improves readability, even if it means some duplication in filter building functions in the authorization roles query module.
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 2825
File: apps/dashboard/app/(app)/logs-v2/hooks/use-bookmarked-filters.ts:0-0
Timestamp: 2025-01-30T20:51:44.359Z
Learning: The user (ogzhanolguncu) prefers to handle refactoring suggestions in separate PRs to maintain focus in the current PR.
📚 Learning: 2025-10-30T15:10:52.743Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 4190
File: go/internal/services/keys/verifier.go:51-53
Timestamp: 2025-10-30T15:10:52.743Z
Learning: PR #4190 for unkeyed/unkey is focused solely on database schema and query changes for identity-based credits. It adds IdentityCredits and KeyCredits fields to structs and queries, but does not implement the priority enforcement logic in the usagelimiter. The logic implementation is intentionally deferred to a later PR in the stack.

Applied to files:

  • go/apps/api/routes/v2_identities_get_identity/handler.go
  • go/apps/api/routes/v2_keys_verify_key/handler.go
  • go/pkg/db/key_data.go
  • go/apps/api/routes/v2_keys_get_key/handler.go
📚 Learning: 2025-08-08T15:09:01.312Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3753
File: go/apps/api/openapi/config.yaml:9-10
Timestamp: 2025-08-08T15:09:01.312Z
Learning: Repo unkeyed/unkey: For go/apps/api/openapi, oapi-codegen is used and does not support OpenAPI 3.1 union types like [T, "null"]; an overlay step is required to downconvert to 3.0-style nullable before code generation.

Applied to files:

  • go/apps/api/routes/v2_identities_get_identity/handler.go
  • go/pkg/db/custom_types.go
  • go/apps/api/routes/v2_keys_verify_key/handler.go
  • go/pkg/db/key_data.go
  • go/apps/api/routes/v2_keys_get_key/handler.go
📚 Learning: 2025-08-21T15:54:45.198Z
Learnt from: chronark
Repo: unkeyed/unkey PR: 3825
File: go/internal/services/usagelimiter/limit.go:38-0
Timestamp: 2025-08-21T15:54:45.198Z
Learning: In go/internal/services/usagelimiter/limit.go, the UpdateKeyCreditsDecrement operation cannot be safely wrapped with db.WithRetry due to the lack of idempotency mechanisms in the current tech stack. Retrying this non-idempotent write operation risks double-charging users if the first attempt commits but the client sees a transient error.

Applied to files:

  • go/apps/api/routes/v2_identities_get_identity/handler.go
📚 Learning: 2025-07-16T15:38:53.491Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.491Z
Learning: For debugging database replica usage in go/pkg/db/replica.go, it's acceptable to mark QueryRowContext operations as "success" even though SQL errors only surface during row.Scan() calls. The timing metrics are the primary concern for debugging replica performance patterns.

Applied to files:

  • go/pkg/db/custom_types.go
📚 Learning: 2025-08-14T18:31:49.604Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3785
File: go/pkg/db/querier_generated.go:387-403
Timestamp: 2025-08-14T18:31:49.604Z
Learning: In MySQL's JSON_OBJECT function, boolean expressions like `rl.auto_apply = 1` automatically convert to proper JSON boolean values (true/false), not numeric values (0/1). This means Go's json.Unmarshal can correctly handle these fields when unmarshalling into bool types without any conversion issues.

Applied to files:

  • go/pkg/db/custom_types.go
📚 Learning: 2025-08-08T15:09:01.312Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3753
File: go/apps/api/openapi/config.yaml:9-10
Timestamp: 2025-08-08T15:09:01.312Z
Learning: Repo unkeyed/unkey: In go/apps/api/openapi, oapi-codegen doesn’t support OpenAPI 3.1 union nullability; overlay.yaml must be applied before codegen. The overlay key in oapi-codegen config isn’t supported—use a pre-step (programmatic or CLI) to merge overlay into the bundled spec, then run oapi-codegen.

Applied to files:

  • go/apps/api/routes/v2_keys_verify_key/handler.go
  • go/pkg/db/key_data.go
  • go/apps/api/routes/v2_keys_get_key/handler.go
📚 Learning: 2025-08-08T15:09:01.312Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3753
File: go/apps/api/openapi/config.yaml:9-10
Timestamp: 2025-08-08T15:09:01.312Z
Learning: Repo unkeyed/unkey: In go/apps/api/openapi, overlays are required to downconvert 3.1 union nullability to 3.0-style nullable before running oapi-codegen; config.yaml’s output-options.overlay is not recognized by oapi-codegen, so overlays must be applied in a pre-step (programmatic or CLI) prior to codegen.

Applied to files:

  • go/apps/api/routes/v2_keys_verify_key/handler.go
  • go/pkg/db/key_data.go
  • go/apps/api/routes/v2_keys_get_key/handler.go
📚 Learning: 2025-08-27T14:08:31.731Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3841
File: go/apps/api/routes/v2_keys_migrate_keys/handler.go:60-65
Timestamp: 2025-08-27T14:08:31.731Z
Learning: In the Unkey Go codebase, service methods like GetRootKey that return an emit function alongside potential errors are designed to always return a safe no-op emit function (emptyLog), even when the main operation fails. This makes it safe to defer emit() immediately after the call, before checking the error. This pattern is consistently used across all API route handlers.

Applied to files:

  • go/apps/api/routes/v2_keys_verify_key/handler.go
📚 Learning: 2025-08-08T16:10:00.224Z
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/workflows/job_detect_changes.yaml:111-114
Timestamp: 2025-08-08T16:10:00.224Z
Learning: Repo: unkeyed/unkey — When imeyer writes “make me an issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Repro, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.

Applied to files:

  • go/apps/api/routes/v2_keys_verify_key/handler.go
📚 Learning: 2025-08-14T16:25:48.167Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3785
File: go/apps/api/routes/v2_keys_reroll_key/401_test.go:52-61
Timestamp: 2025-08-14T16:25:48.167Z
Learning: User Flo4604 requested creation of a GitHub issue to track converting all test files to use table-driven test patterns as a broader codebase improvement, following the suggestion made during review of go/apps/api/routes/v2_keys_reroll_key/401_test.go.

Applied to files:

  • go/apps/api/routes/v2_keys_verify_key/handler.go
📚 Learning: 2025-08-08T15:10:46.436Z
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: Repo: unkeyed/unkey — Preference: If imeyer comments “issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.

Applied to files:

  • go/apps/api/routes/v2_keys_verify_key/handler.go
📚 Learning: 2025-08-08T14:59:52.283Z
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/workflows/runbook-freshness-check.yaml:157-173
Timestamp: 2025-08-08T14:59:52.283Z
Learning: Repo unkeyed/unkey: When a CI/workflow fix is deferred, imeyer prefers a thorough GitHub issue be opened with sections (Summary, Impact, Where, Repro, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References) and assigned to imeyer, including backlinks to the originating PR and comment.

Applied to files:

  • go/apps/api/routes/v2_keys_verify_key/handler.go
📚 Learning: 2025-07-15T14:59:30.212Z
Learnt from: chronark
Repo: unkeyed/unkey PR: 3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Applied to files:

  • go/apps/api/routes/v2_identities_create_identity/200_test.go
🧬 Code graph analysis (6)
go/apps/api/routes/v2_identities_get_identity/handler.go (1)
go/pkg/db/custom_types.go (2)
  • UnmarshalNullableJSONTo (54-75)
  • RatelimitInfo (25-33)
go/apps/api/routes/v2_identities_list_identities/handler.go (2)
go/apps/api/openapi/gen.go (3)
  • V2IdentitiesListIdentitiesRequestBody (708-714)
  • V2IdentitiesListIdentitiesResponseBody (717-726)
  • Meta (279-282)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (54-75)
go/apps/api/routes/v2_keys_verify_key/handler.go (3)
go/apps/api/openapi/gen.go (2)
  • Meta (279-282)
  • Identity (161-173)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (54-75)
go/pkg/db/models_generated.go (2)
  • Key (640-664)
  • Identity (629-638)
go/apps/api/routes/v2_identities_create_identity/200_test.go (1)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (54-75)
go/pkg/db/key_data.go (1)
go/pkg/db/custom_types.go (4)
  • UnmarshalNullableJSONTo (54-75)
  • RoleInfo (12-16)
  • PermissionInfo (18-23)
  • RatelimitInfo (25-33)
go/apps/api/routes/v2_keys_get_key/handler.go (3)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (54-75)
go/pkg/db/models_generated.go (2)
  • Identity (629-638)
  • Key (640-664)
go/apps/api/openapi/gen.go (2)
  • Identity (161-173)
  • Meta (279-282)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Go API Local / Test
  • GitHub Check: Build / Build
  • GitHub Check: autofix
🔇 Additional comments (2)
go/pkg/db/custom_types.go (1)

35-74: LGTM! Clean implementation of nullable JSON unmarshalling.

The function correctly handles all edge cases (nil data, empty bytes, type assertion failures, unmarshal errors) and returns descriptive errors for each failure mode. The generic signature with error return allows callers to decide how to handle failures appropriately.

go/apps/api/routes/v2_identities_create_identity/200_test.go (1)

141-143: LGTM! Correct usage of the unmarshalling helper in tests.

All test cases properly call UnmarshalNullableJSONTo, check the returned error with require.NoError, and use the result for assertions. The pattern is consistent and correct.

Also applies to: 241-243, 316-318, 425-427

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
go/apps/api/routes/v2_keys_get_key/handler.go (1)

250-258: Only assign Meta when map is non-empty to respect omitempty.

The code assigns response.Meta = &meta even when the unmarshalled map is empty, resulting in "meta": {} in the response. This issue has already been flagged in a previous review comment with the correct fix.

🧹 Nitpick comments (1)
go/apps/api/routes/v2_apis_list_keys/handler.go (1)

319-322: LGTM – Clean refactoring to centralized helper.

The migration to db.UnmarshalNullableJSONTo correctly handles nil/empty checks and follows the agreed log-and-continue pattern for meta parsing failures.

Minor enhancement: consider adding the identity ID to the error log for consistency with the key meta error logging at line 391, which includes keyId. This would improve debugging context.

-			if identityMeta, err := db.UnmarshalNullableJSONTo[map[string]any](keyData.Identity.Meta); err != nil {
-				h.Logger.Error("failed to unmarshal identity meta", "error", err)
+			if identityMeta, err := db.UnmarshalNullableJSONTo[map[string]any](keyData.Identity.Meta); err != nil {
+				h.Logger.Error("failed to unmarshal identity meta",
+					"identityId", keyData.Identity.ID,
+					"error", err,
+				)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 648afb4 and 858b26a.

📒 Files selected for processing (3)
  • go/apps/api/routes/v2_apis_list_keys/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_get_key/handler.go (2 hunks)
  • go/apps/api/routes/v2_keys_whoami/handler.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • go/apps/api/routes/v2_keys_whoami/handler.go
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 4190
File: go/internal/services/keys/verifier.go:51-53
Timestamp: 2025-10-30T15:10:52.743Z
Learning: PR #4190 for unkeyed/unkey is focused solely on database schema and query changes for identity-based credits. It adds IdentityCredits and KeyCredits fields to structs and queries, but does not implement the priority enforcement logic in the usagelimiter. The logic implementation is intentionally deferred to a later PR in the stack.
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3785
File: go/apps/api/routes/v2_keys_reroll_key/401_test.go:52-61
Timestamp: 2025-08-14T16:25:48.167Z
Learning: User Flo4604 requested creation of a GitHub issue to track converting all test files to use table-driven test patterns as a broader codebase improvement, following the suggestion made during review of go/apps/api/routes/v2_keys_reroll_key/401_test.go.
Learnt from: imeyer
Repo: unkeyed/unkey PR: 3755
File: .github/actions/setup-node/action.yaml:0-0
Timestamp: 2025-08-08T15:10:46.436Z
Learning: Repo: unkeyed/unkey — Preference: If imeyer comments “issue” on a PR thread, automatically open a thorough GitHub issue (Summary, Impact, Where, Observed vs Expected, Acceptance Criteria, Validation Plan, Out of Scope, References), include backlinks to the PR and comment, and assign to imeyer.
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 3297
File: apps/dashboard/lib/trpc/routers/authorization/roles/query.ts:210-323
Timestamp: 2025-06-04T20:13:12.060Z
Learning: The user ogzhanolguncu prefers explicit, duplicated code over abstracted helper functions when it improves readability, even if it means some duplication in filter building functions in the authorization roles query module.
Learnt from: ogzhanolguncu
Repo: unkeyed/unkey PR: 2825
File: apps/dashboard/app/(app)/logs-v2/hooks/use-bookmarked-filters.ts:0-0
Timestamp: 2025-01-30T20:51:44.359Z
Learning: The user (ogzhanolguncu) prefers to handle refactoring suggestions in separate PRs to maintain focus in the current PR.
📚 Learning: 2025-08-08T15:09:01.312Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3753
File: go/apps/api/openapi/config.yaml:9-10
Timestamp: 2025-08-08T15:09:01.312Z
Learning: Repo unkeyed/unkey: For go/apps/api/openapi, oapi-codegen is used and does not support OpenAPI 3.1 union types like [T, "null"]; an overlay step is required to downconvert to 3.0-style nullable before code generation.

Applied to files:

  • go/apps/api/routes/v2_apis_list_keys/handler.go
  • go/apps/api/routes/v2_keys_get_key/handler.go
📚 Learning: 2025-08-08T15:09:01.312Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3753
File: go/apps/api/openapi/config.yaml:9-10
Timestamp: 2025-08-08T15:09:01.312Z
Learning: Repo unkeyed/unkey: In go/apps/api/openapi, overlays are required to downconvert 3.1 union nullability to 3.0-style nullable before running oapi-codegen; config.yaml’s output-options.overlay is not recognized by oapi-codegen, so overlays must be applied in a pre-step (programmatic or CLI) prior to codegen.

Applied to files:

  • go/apps/api/routes/v2_apis_list_keys/handler.go
  • go/apps/api/routes/v2_keys_get_key/handler.go
📚 Learning: 2025-08-08T15:09:01.312Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 3753
File: go/apps/api/openapi/config.yaml:9-10
Timestamp: 2025-08-08T15:09:01.312Z
Learning: Repo unkeyed/unkey: In go/apps/api/openapi, oapi-codegen doesn’t support OpenAPI 3.1 union nullability; overlay.yaml must be applied before codegen. The overlay key in oapi-codegen config isn’t supported—use a pre-step (programmatic or CLI) to merge overlay into the bundled spec, then run oapi-codegen.

Applied to files:

  • go/apps/api/routes/v2_apis_list_keys/handler.go
  • go/apps/api/routes/v2_keys_get_key/handler.go
📚 Learning: 2025-10-30T15:10:52.743Z
Learnt from: Flo4604
Repo: unkeyed/unkey PR: 4190
File: go/internal/services/keys/verifier.go:51-53
Timestamp: 2025-10-30T15:10:52.743Z
Learning: PR #4190 for unkeyed/unkey is focused solely on database schema and query changes for identity-based credits. It adds IdentityCredits and KeyCredits fields to structs and queries, but does not implement the priority enforcement logic in the usagelimiter. The logic implementation is intentionally deferred to a later PR in the stack.

Applied to files:

  • go/apps/api/routes/v2_keys_get_key/handler.go
🧬 Code graph analysis (2)
go/apps/api/routes/v2_apis_list_keys/handler.go (3)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (54-80)
go/apps/api/openapi/gen.go (2)
  • Identity (161-173)
  • Meta (279-282)
go/pkg/db/models_generated.go (2)
  • Identity (643-652)
  • Key (654-678)
go/apps/api/routes/v2_keys_get_key/handler.go (2)
go/pkg/db/custom_types.go (1)
  • UnmarshalNullableJSONTo (54-80)
go/pkg/db/models_generated.go (2)
  • Identity (643-652)
  • Key (654-678)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build / Build
  • GitHub Check: Test Go API Local / Test
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
go/apps/api/routes/v2_apis_list_keys/handler.go (1)

388-395: LGTM – Proper error context and type handling.

The refactoring correctly handles sql.NullString by checking Valid before accessing String, and the error log includes keyId for effective debugging. The helper seamlessly handles the string-to-bytes conversion needed for JSON unmarshalling.

@Flo4604 Flo4604 self-requested a review November 6, 2025 14:51
@Flo4604 Flo4604 added this pull request to the merge queue Nov 6, 2025
@graphite-app
Copy link

graphite-app bot commented Nov 6, 2025

TV gif. Steve Irwin the Crocodile Hunter looking down at a body of water, turns around and gives a double thumbs-up, mouthing 'that's good.' (Added via Giphy)

Merged via the queue into main with commit 534661a Nov 6, 2025
20 checks passed
@Flo4604 Flo4604 deleted the ENG-1991 branch November 6, 2025 14:53
@graphite-app
Copy link

graphite-app bot commented Nov 6, 2025

Graphite Automations

"Post a GIF when PR approved" took an action on this PR • (11/06/25)

1 gif was posted to this PR based on Andreas Thomas's automation.

@coderabbitai coderabbitai bot mentioned this pull request Nov 10, 2025
19 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve type safety for JSON columns in database structs

4 participants